package app 
{
	import app.textures.TextureInfo;
	import draw.IPalette;
	import drawing.FurniturePaletteProvider;
	import events.Scene3DEvent;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.filters.GlowFilter;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import furnitures.Furniture3D;
	import furnitures.FurnitureAlter;
	import furnitures.FurnitureModel;
	import resources.Images;
	import simple3D.core.DisplayObject3D;
	import simple3D.core.ShapeData;
	import simple3D.core.ShapeFactory;
	import simple3D.core.Surface3D;
	import simple3D.geom.Factory;
	import simple3D.geom.Line2D;
	import simple3D.geom.Plane;
	import simple3D.materials.BitmapMaterial;
	import simple3D.utils.MathUtil;
	import utils.ErrorsCollector;
	import utils.Logger;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	
	 /**
	  * When scene3D complete redraw graphics
	  */
	[Event(name = "SCENE_UPDATED", type = "events.Scene3DEvent")]
	public class Scene3D extends Sprite
	{
		private var _scene3D:DisplayObject3D;
		
		private var furnitureMove:Furniture3D = null;
		private var furnitureSelected:Furniture3D = null;
		private var _furnitures:Vector.<Furniture3D>;
		private var floorSize:Number;
		private var furnitureHolder:DisplayObject3D;
		private var guideLine:Line2D;
		private var dragOffset:Vector3D;
		
		private const FILTER_MOVE:GlowFilter = new GlowFilter(0x00FF00);
		private const FILTER_SELECTED:GlowFilter = new GlowFilter(0xCC00CC);
		private var planeYZ:Plane;
		private var planeXZ:Plane;
		private var planeTarget:Plane;
		
		private var direction:String;
		private var coordShape:Shape;
		
		private var _roomWidth:Number = 100;
		private var _roomHeight:Number = 100;
		private var _roomLenght:Number = 100;
		
		private var _roomWidthOrign:Number = 100;
		private var _roomHeightOrign:Number = 100;
		private var _roomLenghtOrign:Number = 100;
		
		private var rightWall:Surface3D;
		private var leftWall:Surface3D;
		private var floor:Surface3D;
		protected var _isInvalidation:Boolean = false;
		
		//private var _scaleFactor:Number = 1;
		private var _scaleFactorAxis:Number = 1;
		private var _tileSize:Number;
		
		private static const MARK_SELECTED:String = "MARK_SELECTED";
		private static const MARK_NORMAL:String = "MARK_NORMAL";
		
		
		public function Scene3D(startScale:Number, tileSize:Number = 50)
		{
			
			_scaleFactorAxis = startScale;
			_tileSize = tileSize;
			log("_tileSize: " + _tileSize);
			addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
		}
		
		private function handleAddedToStage(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
			initScene();
		}
		
		private function initScene():void 
		{
			_furnitures = new Vector.<Furniture3D>();
			_scene3D = new DisplayObject3D();
			_scene3D.x = 350;
			_scene3D.y = 250;
			// set scaling
			_scene3D.scale.x = _scaleFactorAxis;
			_scene3D.scale.y = _scaleFactorAxis;
			_scene3D.scale.z = _scaleFactorAxis;
			
			addChild(_scene3D);
			furnitureHolder = new DisplayObject3D();
			_scene3D.addChild(furnitureHolder);
			furnitureHolder.angle = new Vector3D(2.6, 0.6, 2.8);
			
			updateWalls();
			//addEventListener(Event.ENTER_FRAME, loop);
			guideLine = new Line2D();
			coordShape = new Shape();
			//addChild(coordShape);
			invalidate();
			stage.addEventListener(MouseEvent.MOUSE_UP, handleStageMouseUp);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, handleStageMouseMove);
			
			rightWall.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			leftWall.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			floor.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			
		}
		
		private function handleWallDown(e:MouseEvent):void 
		{
			_scene3D.startDrag();
			if (furnitureSelected != null)
			{
				furnitureSelected.filters = [];
				furnitureSelected = null;
			}
		}
		
		
		public function updateWalls():void
		{
			// clear listeners
			if (rightWall != null)
			{
				rightWall.removeEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			}
			if (leftWall != null)
			{
				leftWall.removeEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			}
			if (floor != null)
			{
				floor.removeEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			}
			
			safeRemoveObject(floor);
			safeRemoveObject(leftWall);
			safeRemoveObject(rightWall);
			
			// размер плитки
			log("set _tileSize: " + _tileSize);
			log("_roomHeight: " + _roomHeight);
			var textureSize:Number = _tileSize; // 500 millimeters
			/*var shapeTexture:Shape = new Shape();
			var gr:Graphics = shapeTexture.graphics;
			gr.lineStyle(2, 0x000000);
			gr.beginFill(0xFFFFCC, 1);
			gr.drawRect(0, 0, textureSize, textureSize);
			gr.endFill();*/
			
			var shapeTexture:DisplayObject = Images.getTile();
			shapeTexture.width = _tileSize;
			shapeTexture.height = _tileSize;
			var texture:BitmapData = new BitmapData(shapeTexture.width, shapeTexture.height, true, 0xFFFFCC);
			texture.draw(shapeTexture);
			
			// floor
			floor = createPlane(_roomLenght, _roomWidth, texture);
			floor.angle.y = 180* Math.PI / 180;
			floor.angle.z = 180* Math.PI / 180;
			//floor.position.x = roomLenght;
			floor.position.z = _roomWidth;
			floor.setCentrPoint(0, 0, _roomWidth);
			furnitureHolder.addChild(floor);
			
			//left wall
			leftWall = createPlane(_roomLenght, _roomHeight, texture);
			leftWall.angle.x = -90 * Math.PI / 180;
			leftWall.position.y = -_roomHeight;
			leftWall.setCentrPoint(0, 0, _roomHeight);
			furnitureHolder.addChild(leftWall);
			
			//right wall
			rightWall = createPlane(_roomWidth, _roomHeight, texture);
			rightWall.angle.y = -90 * Math.PI / 180;
			rightWall.angle.z = -90 * Math.PI / 180;
			rightWall.position.y = -_roomHeight;
			rightWall.setCentrPoint(0, 0, _roomHeight);
			furnitureHolder.addChild(rightWall);
			
			rightWall.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			leftWall.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
			floor.addEventListener(MouseEvent.MOUSE_DOWN, handleWallDown);
		}
		
		private function safeRemoveObject(obj:DisplayObject):void
		{
			if (obj != null && obj.parent != null)
			{
				obj.parent.removeChild(obj);
			}
		}
		private function createPlane(planeW:Number, planeH:Number, texture:BitmapData):Surface3D
		{
			var plane:Surface3D;
			
			var planeData:ShapeData = ShapeFactory.getPlaneDataXZ(new Point(0, 0), 
																new Point(planeW, planeH), 
																0);
			var shapeTexture:Shape = new Shape();
			var gr:Graphics = shapeTexture.graphics;
			gr.clear();
			gr.beginBitmapFill(texture, null, true, true);
			gr.drawRect(0, 0, planeW, planeH);
			gr.endFill();
			
			var textureBig:BitmapData = new BitmapData(shapeTexture.width, shapeTexture.height, true, 0xFFFFCC);
			textureBig.draw(shapeTexture);
			
			return new Surface3D(planeData.points, planeData.indices, planeData.uvt, 
									new BitmapMaterial(textureBig, false));
			
		}
		
		private function handleStageMouseUp(e:MouseEvent):void 
		{
			_scene3D.stopDrag();
			
			if (furnitureMove != null)
			{
				furnitureMove.filters = [FILTER_SELECTED];
				furnitureMove = null;
			}
		}
		
		
		public function addFurnitureWithPalette(furnModel:FurnitureModel, 
												facade:IPalette, 
												tabletop:IPalette, 
												corpus:IPalette,
												tech:IPalette):void
		{
			if (furnModel == null)
			{
				return;
			}
			var furniture:Furniture3D = new Furniture3D(furnModel, AppSettings.SCALE_FACTOR);
			furniture.addEventListener(MouseEvent.MOUSE_DOWN, handleFurnitureMouseDown);
			
			
			furniture.setFacadePalette(facade, tech);

			if (tabletop != null)
			{
				furniture.setTabletopPalette(tabletop);
			}
			
			if (corpus != null)
			{
				furniture.setCorpusPalette(corpus);
			}
			
			
			
			furnitureHolder.addChild(furniture);
			furniture.angle.y = 90 * Math.PI / 180;
			furniture.position.y = furniture.positionY;
			_furnitures.push(furniture);
			
			furniture.position.x = 150;
			furniture.position.z = 150;
			invalidate();
		}
		
		private function createFurniture(model:FurnitureModel, 
										parentObj:DisplayObject3D):Furniture3D
		{
			
			var furniture:Furniture3D = new Furniture3D(model, AppSettings.SCALE_FACTOR);
			furniture.addEventListener(MouseEvent.MOUSE_DOWN, handleFurnitureMouseDown);
			//furniture.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);
			parentObj.addChild(furniture);
			//furniture.position.x = 250;
			//furniture.position.z = 250;
			furniture.angle.y = 90 * Math.PI / 180;
			furniture.position.y = furniture.positionY;
			//furniture.angle.z = 90 * Math.PI / 180;
			_furnitures.push(furniture);
			return furniture;
		}
		
		private function handleFurnitureMouseDown(e:MouseEvent):void 
		{
			var furniture:Furniture3D = e.currentTarget as Furniture3D;
			if (furnitureSelected != null)
			{
				furnitureSelected.filters = [];
			}
			if (furniture != null)
			{
				furnitureSelected = furniture;
				furnitureMove = furniture;
				furnitureMove.filters = [FILTER_MOVE];
				// calc offset
				var xPos:Number = (_scene3D.x - mouseX);
				var yPos:Number = (_scene3D.y - mouseY);
				var p:Vector3D = getIntersectionPoint(xPos, yPos, furnitureHolder);
				var pFloor:Vector3D = furnitureHolder.globalToLocal3D2(p);
				dragOffset = furnitureMove.position.clone();
				dragOffset.decrementBy(pFloor);
				
				var p1:Point = furnitureHolder.getPointOnScreen(pFloor);
				p1.x = p1.x + _scene3D.x; 
				p1.y = p1.y + _scene3D.y; 
				var pAxis:Vector3D = pFloor.clone();
				if (furnitureMove.position.x >furnitureMove.position.z)
				{
					pAxis.x += 50;
					furnitureMove.angle.y = 0;
					direction = "x";
				}else
				{
					pAxis.z += 50;
					direction = "z";
					furnitureMove.angle.y = 90*Math.PI/180;
				}
				
				
				var p2:Point = furnitureHolder.getPointOnScreen(pAxis)
				p2.x = p2.x + _scene3D.x; 
				p2.y = p2.y + _scene3D.y; ;
				guideLine.buildFromPoints(p1, p2);
			}
			
		}
		
	
		
		private function drawLine(gr:Graphics, p1:Point, p2:Point):void
		{
			gr.clear();
			gr.lineStyle(1, 0xFF0000);
			gr.moveTo(p1.x, p1.y);
			gr.lineTo(p2.x, p2.y);
		}
	
		private function handleStageMouseMove(e:MouseEvent):void 
		{
			if (furnitureMove != null)
			{
				var xPos:Number = (_scene3D.x - mouseX);
				//var yPos:Number = (scene3D.y - mouseY);
				var yPos:Number = _scene3D.y - guideLine.getYCoordAtX(mouseX);
				tryMoveTo(xPos, yPos);
				//trace("yPos: " + yPos);
				//trace("Mouse: " + xPos + " " + yPos);
				/*var invertPerspective:Number = 1;
				planeXZ = Factory.planeFromPoints(new Vector3D(xPos * invertPerspective, 
																yPos*invertPerspective, 0), 
												  new Vector3D((1 + xPos)*invertPerspective, yPos*invertPerspective, 0),
												  new Vector3D(xPos * invertPerspective, 
																yPos*invertPerspective, 1));
				planeYZ = Factory.planeFromPoints(new Vector3D(xPos * invertPerspective, 
																yPos*invertPerspective, 0), 
												  new Vector3D(xPos * invertPerspective, 
																(1 + yPos)*invertPerspective, 0),
												  new Vector3D(xPos * invertPerspective, 
																yPos*invertPerspective, 1));
				planeTarget = Factory.planeFromPoints(furnitureHolder.rotateAndTranslate(new Vector3D(0, 0, 0)), 
														furnitureHolder.rotateAndTranslate(new Vector3D(0, 0, 1)),
														furnitureHolder.rotateAndTranslate(new Vector3D(1, 0, 0)));
				//var p:Vector3D = MathUtil.getIntersectPlane(planeXZ, planeYZ, planeTarget);
				var p:Vector3D = getIntersectionPoint(xPos, yPos, furnitureHolder);
				//trace("intersect: " + p.x + " " + p.y + " " + p.z);
				var pFloor:Vector3D = furnitureHolder.globalToLocal3D2(p);
				//trace("point floor: " + pFloor.x + " " + pFloor.y + " " + pFloor.z);
				pFloor.incrementBy(dragOffset);
				pFloor.y = furnitureMove.position.y;
				//pFloor.z = furnitureMove.position.z;
				if (direction == "x")
				{
					pFloor.z = 0;
				}else
				{
					pFloor.x = 0;
				}
				//trace("Direction: " + direction + " Move to: " + pFloor.x + " " + pFloor.y + " " + pFloor.z);
				if (furnitureMove != null && pFloor!=null)
				{
					//trace("move furniture: " + pFloor.x + " " + pFloor.y + " " + pFloor.z);
					//furnitureMove.moveTo(pFloor);
					tryMoveTo(xPos, yPos);
				}else if (p == null)
				{
					trace("Intersect point: null");
				}
				*/
				if ((direction == "x") && (furnitureMove.position.x < 0))
				{
					swapGuideLine(new Vector3D(0, 0, 20));
					direction = "z";
					furnitureMove.angle.y = 90 * Math.PI / 180;
				}else if ((direction == "z") && (furnitureMove.position.z - furnitureMove.furnitureWidth < 0))
				{
					swapGuideLine(new Vector3D(20, 0, 0));
					direction = "x";
					furnitureMove.angle.y = 0;
				}
				//trace("=============");
				invalidate();
			}
			
		}
		
		private function tryMoveTo(xPos:Number, yPos:Number):void
		{
			// get furnitures by AXIS
			var currentAxisFurn:Vector.<Furniture3D> = new Vector.<Furniture3D>();
			for each(var furn:Furniture3D in _furnitures)
			{
				// exclude current moving furniture
				if (furn == furnitureMove)
				{
					continue;
				}
				if (direction == "x")
				{
					if (furn.position.x > furn.position.z) // axis X
					{
						currentAxisFurn.push(furn);
					}
				}else if (direction == "z")
				{
					if (furn.position.x < furn.position.z) // axis Z
					{
						currentAxisFurn.push(furn);
					}
				}
			}
			
			// check can place furniture
			planeXZ = Factory.planeFromPoints(new Vector3D(xPos , 
															yPos, 0), 
											  new Vector3D((1 + xPos), yPos, 0),
											  new Vector3D(xPos , 
															yPos, 1));
			planeYZ = Factory.planeFromPoints(new Vector3D(xPos , 
															yPos, 0), 
											  new Vector3D(xPos , 
															(1 + yPos), 0),
											  new Vector3D(xPos , 
															yPos, 1));
			planeTarget = Factory.planeFromPoints(furnitureHolder.rotateAndTranslate(new Vector3D(0, 0, 0)), 
													furnitureHolder.rotateAndTranslate(new Vector3D(0, 0, 1)),
													furnitureHolder.rotateAndTranslate(new Vector3D(1, 0, 0)));
			var p:Vector3D = getIntersectionPoint(xPos, yPos, furnitureHolder);
			var pFloor:Vector3D = furnitureHolder.globalToLocal3D2(p); // position furniture on the floor/room
			pFloor.incrementBy(dragOffset);
			pFloor.y = furnitureMove.position.y;
			//pFloor.z = furnitureMove.position.z;
			// place to axis
			if (direction == "x")
			{
				pFloor.z = 0;
			}else
			{
				pFloor.x = 0;
			}
			
			var axisPropName:String = direction;
			var sideDirection:Number = 1;
			if (axisPropName == "z")
			{
				sideDirection = -1;
			}
			// check intersetion for new position
			var newPos:Vector3D = pFloor.clone();
			//newPos.x += pFloor.x;
			//newPos.z += pFloor.z;
			var pointLeftUpper:Point = new Point(newPos[axisPropName], 
													furnitureMove.position.y);
			var pointRightDown:Point = new Point(newPos[axisPropName] 
													+ furnitureMove.furnitureWidth * sideDirection, 
													furnitureMove.position.y + furnitureMove.furnitureHeight);
			
			
			var pointCheckLU:Point; // left upper
			var pointCheckRD:Point; // right down
			var intersectWith:Furniture3D = null;
			var furnForCheck:Furniture3D;
			
			for each (furnForCheck in currentAxisFurn)
			{
				
				// check bounding box intersection
				
				pointCheckLU = new Point(furnForCheck.position[axisPropName], furnForCheck.position.y);
				pointCheckRD = new Point(furnForCheck.position[axisPropName] + furnForCheck.furnitureWidth * sideDirection, 
											furnForCheck.position.y + furnForCheck.furnitureHeight); 
				/*trace("chek with: ");
				trace("pos x: " + furnForCheck.position.x + " y: "  + furnForCheck.position.y + " z: " +furnForCheck.position.z);
				trace("chekUpper x: " + pointCheckLU.x + " y: "  + pointCheckLU.y);
				trace("checkDown x: " + pointCheckRD.x + " y: " + pointCheckRD.y);*/
				if (utils.MathUtil.intersectBoundingBox(pointLeftUpper, pointRightDown, pointCheckLU, pointCheckRD))
				{
					intersectWith = furnForCheck;
					break;
				}
			}
			
			if (intersectWith == null)
			{
				if (axisPropName == "x")
				{
					// check lenth room
					if ((pFloor.x+furnitureMove.furnitureWidth) <= roomLenght)
					{
						furnitureMove.moveTo(pFloor);
					}
				}else if (axisPropName == "z")
				{
					// check width room
					if (pFloor.z <= roomWidth)
					{
						furnitureMove.moveTo(pFloor);
					}
				}else
				{
					furnitureMove.moveTo(pFloor);
				}
			}
			
			/*trace("Current mov pos x: " + furnitureMove.position.x + " y: "  
					+ furnitureMove.position.y + " z: " +furnitureMove.position.z + 
					" axis " + axisPropName + ": " + furnitureMove.position[axisPropName]);
				trace("pointLeftUpper x: " + pointLeftUpper.x + " y: "  + pointLeftUpper.y);
				trace("pointRightDown x: " + pointRightDown.x + " y: " + pointRightDown.y);
				*/
			if (intersectWith != null)
			{
				return;
				//trace("Intersect with total: "  + currentAxisFurn.length);
				//trace("chekUpper x: " + pointCheckLU.x + " y: "  + pointCheckLU.y);
				//trace("checkDown x: " + pointCheckRD.x + " y: " + pointCheckRD.y);
				var posNearIntersect:Vector3D = intersectWith.position.clone();
				
				//check to can place in nearest by mouse
				
				var lenToLeft:Number = Math.abs(pointCheckLU.x - pointLeftUpper.x) + 
										Math.abs(pointCheckLU.x - pointCheckRD.x)*0.0;
				var lenToRight:Number = Math.abs(pointCheckRD.x - pointLeftUpper.x);
				var idealPos:Number = lenToLeft < lenToRight ? 
									pointCheckLU.x - furnitureMove.furnitureWidth :
									pointCheckRD.x;
				var idealPosLeft:Number = posNearIntersect[axisPropName] + intersectWith.furnitureWidth * sideDirection;
				var idealPosRigth:Number = posNearIntersect[axisPropName] - furnitureMove.furnitureWidth * sideDirection;
				posNearIntersect.y = furnitureMove.positionY;
				posNearIntersect[axisPropName] = idealPos;
				
				// check can placed
				pointLeftUpper = new Point(posNearIntersect.x, 
													furnitureMove.position.y);
				pointRightDown = new Point(posNearIntersect.x
													+ furnitureMove.furnitureWidth * sideDirection, 
													furnitureMove.position.y + furnitureMove.furnitureHeight);
				currentAxisFurn.splice(currentAxisFurn.indexOf(furnForCheck), 1);
				
				intersectWith = null;
				for each (furnForCheck in currentAxisFurn)
				{
					
					// check bounding box intersection
					pointCheckLU = new Point(furnForCheck.position[axisPropName], furnForCheck.position.y);
					pointCheckRD = new Point(furnForCheck.position[axisPropName] + furnForCheck.furnitureWidth * sideDirection, 
												furnForCheck.position.y + furnForCheck.furnitureHeight); 
					
					if (utils.MathUtil.intersectBoundingBox(pointLeftUpper, pointRightDown, pointCheckLU, pointCheckRD))
					{
						intersectWith = furnForCheck;
						break;
					}
				}
				
				if (intersectWith == null)
				{
					if (axisPropName == "x")
					{
						// check lenth room
						if ((posNearIntersect.x+furnitureMove.furnitureWidth) <= roomLenght)
						{
							furnitureMove.moveTo(posNearIntersect);
						}
					}else if (axisPropName == "z")
					{
						// check width room
						if (posNearIntersect.z <= roomWidth)
						{
							furnitureMove.moveTo(posNearIntersect);
						}
					}
				}
				
				//furnitureMove.moveTo(posNearIntersect);
				//trace("lenToLeft: " + lenToLeft + " lenToRight: " + lenToRight 
							//+ " pointLeftUpper.x: " + pointLeftUpper.x + " pointCheckLU.x: " + pointCheckLU.x );
			}
			
			//trace("========================");
			//clear
			//currentAxisFurn.splice(0, currentAxisFurn.length);
		}
		
		
		private function swapGuideLine(toAxis:Vector3D):void 
		{
			//trace("Swap, furniture pos: " + furnitureMove.position.x + " " + furnitureMove.position.y + 
			//		" " + furnitureMove.position.z);
			furnitureMove.position.x = 0;
			//furnitureMove.position.y = 0;
			furnitureMove.position.z = 0;
			if (direction == "x")
			{
				furnitureMove.position.z = furnitureMove.furnitureWidth;
			}
			// calc offset
			var xPos:Number = (_scene3D.x - mouseX);
			var yPos:Number = (_scene3D.y - mouseY);
			var p:Vector3D = getIntersectionPoint(xPos, yPos, furnitureHolder);
			var pFloor:Vector3D = furnitureHolder.globalToLocal3D2(p);
			dragOffset = furnitureMove.position.clone();
			dragOffset.decrementBy(pFloor);
				
			var p1:Point = furnitureHolder.getPointOnScreen(pFloor);
			p1.x = p1.x + _scene3D.x; 
			p1.y = p1.y + _scene3D.y; 
			var pAxis:Vector3D = pFloor.clone();
			pAxis.incrementBy(toAxis);
			var p2:Point = furnitureHolder.getPointOnScreen(pAxis)
			p2.x = p2.x + _scene3D.x; 
			p2.y = p2.y + _scene3D.y; ;
			guideLine.buildFromPoints(p1, p2);
			drawLine(coordShape.graphics, p1, p2);
		}
		
		private function getIntersectionPoint(xPos:Number, yPos:Number, targetPlane:DisplayObject3D):Vector3D
		{
			var invertPerspective:Number = 1;
			planeXZ = Factory.planeFromPoints(new Vector3D(xPos * invertPerspective, 
															yPos*invertPerspective, 0), 
											  new Vector3D((1 + xPos)*invertPerspective, yPos*invertPerspective, 0),
											  new Vector3D(xPos * invertPerspective, 
															yPos*invertPerspective, 1));
			planeYZ = Factory.planeFromPoints(new Vector3D(xPos * invertPerspective, 
															yPos*invertPerspective, 0), 
											  new Vector3D(xPos * invertPerspective, 
															(1 + yPos)*invertPerspective, 0),
											  new Vector3D(xPos * invertPerspective, 
															yPos*invertPerspective, 1));
			planeTarget = Factory.planeFromPoints(targetPlane.rotateAndTranslate(new Vector3D(0, 0, 0)), 
													targetPlane.rotateAndTranslate(new Vector3D(0, 0, 1)),
													targetPlane.rotateAndTranslate(new Vector3D(1, 0, 0)));
			return MathUtil.getIntersectPlane(planeXZ, planeYZ, planeTarget);
		}
		
		public function removeSelected():void
		{
			if (furnitureSelected != null)
			{
				var index:int = _furnitures.indexOf(furnitureSelected);
				if (index > -1)
				{
					_furnitures.splice(index, 1);
				}
				if (furnitureSelected.parent != null)
				{
					furnitureSelected.parent.removeChild(furnitureSelected);
				}
				furnitureSelected = null;
			}
			invalidate();
		}
		
		public function setFacadePalette(palette:IPalette, techPalette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			for each(var furniture:Furniture3D in _furnitures)
			{
				if (furniture.itemType != FurnitureModel.ITEM_TECH){
					furniture.setFacadePalette(palette, techPalette);
				}
			}
			invalidate();
		}
		
		
		public function setTabletopPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			for each(var furniture:Furniture3D in _furnitures)
			{
				if (furniture.itemType != FurnitureModel.ITEM_TECH){
					furniture.setTabletopPalette(palette);
				}
			}
			invalidate();
		}
		
		public function setCorpusPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			for each(var furniture:Furniture3D in _furnitures)
			{
				if (furniture.itemType != FurnitureModel.ITEM_TECH){
					furniture.setCorpusPalette(palette);
				}
			}
			invalidate();
		}
		
		
		public function setRotationScene(angle:Vector3D):void
		{
			furnitureHolder.angle = angle.clone();
			invalidate();
		}
		
		public function invalidate():void
		{
			if (!_isInvalidation)
			{
				_isInvalidation = true;
				addEventListener(Event.ENTER_FRAME, onInvalidate);
			}
		}
		
		public function validateNow():void
		{
			onInvalidate(null);
		}
		
		protected function onInvalidate(e:Event):void 
		{
			removeEventListener(Event.ENTER_FRAME, onInvalidate);
			_isInvalidation = false;
			_scene3D.update();
			dispatchEvent(new Scene3DEvent(Scene3DEvent.SCENE_UPDATED));
		}
		
		/*private function loop(event:Event):void
		{
			scene3D.update();
		}*/
		
		private function handleError(err:String):void 
		{
			ErrorsCollector.collectError("Scene 3D: " + err);
		}
		
		public function get roomWidth():Number 
		{
			return _roomWidthOrign;
		}
		
		public function set roomWidth(value:Number):void 
		{
			_roomWidthOrign = value;
			//_roomWidth = value * _scaleFactor;
			_roomWidth = value;
		}
		
		public function get roomHeight():Number 
		{
			return _roomHeightOrign;
		}
		
		public function set roomHeight(value:Number):void 
		{
			_roomHeightOrign = value;
			//_roomHeight = value * _scaleFactor;
			_roomHeight = value;
		}
		
		public function get roomLenght():Number 
		{
			return _roomLenghtOrign;
		}
		
		public function set roomLenght(value:Number):void 
		{
			_roomLenghtOrign = value;
			_roomLenght = value;
			//_roomLenght = value * _scaleFactor;
		}
		
		public function get scaleFactor():Number 
		{
			return _scaleFactorAxis;
		}
		
		public function set scaleFactor(value:Number):void 
		{
			_scaleFactorAxis =  value;
			_scene3D.scale.x = value;
			_scene3D.scale.y = value;
			_scene3D.scale.z = value;
			rebuildScene();
		}
		
		public function get scene3D():DisplayObject3D 
		{
			return _scene3D;
		}
		
		private function rebuildScene():void 
		{
			updateWalls();
			invalidate();
		}
		
		public function setRoomSize(w:Number, l:Number, h:Number):void
		{
			roomWidth = w;
			roomLenght = l;
			roomHeight = h;
			updateWalls();
			invalidate();
		}
		
		public function getSizesNeeded():Vector.<String>
		{
			var sizesNeeded:Vector.<String> = new Vector.<String>();
			var sizeForFurniture:Vector.<String>;
			for each(var furniture:Furniture3D in _furnitures)
			{
				sizeForFurniture = furniture.getSizesTexture();
				for each(var sizetype:String in sizeForFurniture)
				{
					sizesNeeded.push(sizetype);
				}
			}
			return sizesNeeded;
		}
		
		private function log(msg:String):void
		{
			Logger.log(this, msg);
		}
		
	}

}